package com.qfmy.aop;

import common.exception.BaseException;
import common.login.LoginUser;
import common.login.LoginUserHolder;
import common.result.ResultCodeEnum;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import model.annotation.RedisLimitAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 清风明月
 * @Package com.qfmy.aop
 * @date 2025/6/25
 * @description 接口限流
 */
@Slf4j
@Aspect
@Component
@SuppressWarnings("all")
public class RedisLimit {

    /**
     * 注入redisTemplate
     */
    @Resource private RedisTemplate<String, Object> redisTemplate;

    /**
     * 注入Lua脚本
     */
    private DefaultRedisScript<Long> redisScript;
    @PostConstruct
    public void init()
    {
        redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
    }

    /**
     * 接口限流 使用Aop + 自定义注解 + 反射 + redis + lua脚本实现
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(model.annotation.RedisLimitAnnotation)")
    public Object log(@NotNull ProceedingJoinPoint joinPoint) throws Throwable
    {
        //回去方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //回去具体的方法
        Method method = signature.getMethod();
        //获取方法的注解
        RedisLimitAnnotation annotation = method.getAnnotation(RedisLimitAnnotation.class);
        //判断是否有注解
        if(annotation != null)
        {
            //获取注解的key
            String key = annotation.key();
            //获取类名
            String className = method.getDeclaringClass().getName();
            //获取方法的名称
            String name = method.getName();
            //获取用户的id
            LoginUser loginUser = LoginUserHolder.getLoginUser();
            if(loginUser == null)
            {
                throw new BaseException(ResultCodeEnum.TOKEN_INVALID);
            }
            Long userId = loginUser.getUserId();
            //构建redis的key
            String limitKey = className + ":" + name + ":" + key + ":" + userId;
            //获取限流的时间
            Long limitTime = annotation.time();
            //获取限流的次数
            Long limitCount = annotation.limit();
            //创建一个集合用来存储key
            List<String> keys = new ArrayList<>();
            keys.add(limitKey);
            //使用lua脚本实现限流的功能
            Long count = redisTemplate.execute(redisScript,
                    //限流key
                    keys,
                    //限流时间
                    limitTime,
                    //限流次数
                    limitCount);
            //判断是否限流
            if(count != null && count > limitCount)
            {
                throw new BaseException(600,annotation.msg());
            }
        }
       try{
           return joinPoint.proceed();
       }catch (Exception e)
       {
           log.error("限流异常:{}",e.getMessage());
           throw new BaseException(ResultCodeEnum.SERVICE_ERROR);
       }
    }

}
